/**
 * CustomEvent constructor polyfill for IE
 */
const polyfillCustomEvent = () => {
  // 如果不是IE
  if (typeof window.CustomEvent === 'function') return false
  let CustomEvent = function (event, params) {
    params = params || {
      bubbles: false,
      cancelable: false,
      detail: undefined
    }
    let evt = document.createEvent('CustomEvent')
    evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail)
    return evt
  }
  CustomEvent.prototype = window.Event.prototype
  window.CustomEvent = CustomEvent
}

/**
 * RequestAnimationFrame constructor polyfill
 */
const polyfillRequestAnimationFrame = () => {
  if (!window.requestAnimationFrame) {
    window.requestAnimationFrame = (
      window.webkitRequestAnimationFrame ||
      window.mozRequestAnimationFrame ||
      window.msRequestAnimationFrame ||
      function (callback) {
        return window.setTimeout(callback, 1000/60)
      }
    )
  }
}

/**
 * CancelAnimationFrame constructor polyfill
 */
const polyfillCancelAnimationFrame = () => {
  if (!window.cancelAnimationFrame) {
    window.cancelAnimationFrame = (
      window.mozCancelAnimationFrame ||
      function (requestID) {
        return window.clearTimeout(requestID)
      }
    )
  }
}

/**
 * Promise constructor polyfill
 */
const polyfillPromise = () => {
  if (window.Promise) return
  function myPromise (executor) {
    const _this = this
    this.status = 'pending' // 状态
    this.value = undefined  // 成功结果
    this.reason = undefined // 失败原因
    this.onFulfilled = []   // 成功的回调
    this.onRejected = []    // 失败的回调
    function resolve (value) {
      if (_this.status === 'pending') {
        _this.status = 'fulfilled'
        _this.value = value
        _this.onFulfilled.forEach(fn => fn(value))
      }
    }
    function reject (reason) {
      if (_this.status === 'pending') {
        _this.status = 'rejected'
        _this.reason = reason
        _this.onRejected.forEach(fn => fn(reason))
      }
    }
    try {
      executor(resolve, reject)
    } catch (e) {
      reject(e)
    }
  }
  /**
   * @param promise2 新的Promise对象
   * @param x 上一个then的返回值
   * @param resolve promise2的resolve
   * @param reject promise2的reject
   */
  function resolvePromise(promise2, x, resolve, reject) {
    if (promise2 === x) {
      reject(new TypeError('Chaining cycle'))
    }
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
      // 函数或对象
      let used
      try {
        let then = x.then
        if (typeof then === 'function') {
          then.call(x, (y) => {
            if (used) return
            used = true
            resolvePromise(promise2, y, resolve, reject)
          }, (r) =>{
            if (used) return
            used = true
            reject(r)
          })
        } else {
          if (used) return
          used = true
          resolve(x)
        }
      } catch (e) {
        if (used) return
        used = true
        reject(e)
      }
    } else {
      // 普通值
      resolve(x)
    }
  }
  myPromise.prototype.then = function (onFulfilled, onRejected) {
    const _this = this
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
    onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason}
    let promise2 = new myPromise((resolve, reject) => {
      if (_this.status === 'fulfilled') {
        setTimeout(()=>{
          try {
            let x = onFulfilled(_this.value)
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      } else if (_this.status === 'rejected') {
        setTimeout(()=>{
          try {
            let x = onFulfilled(_this.reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (error) {
            reject(error)
          }
        })
      } else if (_this.status === 'pending') {
        _this.onFulfilled.push(()=>{
          setTimeout(()=>{
            try {
              let x = onFulfilled(_this.value)
              resolvePromise(promise2, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          })
        })
        _this.onRejected.push(()=>{
          setTimeout(()=>{
            try {
              let x = onRejected(_this.reason)
              resolvePromise(promise2, x, resolve, reject)
            } catch (error) {
              reject(error)
            }
          })
        })
      }
    })
    return promise2
  }
  window.Promise = myPromise
}

module.exports = {
  polyfillCustomEvent,
  polyfillRequestAnimationFrame,
  polyfillCancelAnimationFrame,
  polyfillPromise
}
